package com.ys.bean.factory;

import com.ys.bean.support.BeanDefinition;
import com.ys.bean.support.BeanDefinitionRegistry;
import com.ys.enumeration.BeanScope;
import com.ys.exception.BeanDefinitionStoreException;
import com.ys.exception.BeansException;
import com.ys.exception.InitializationException;
import com.ys.exception.NoSuchBeanDefinitionException;
import com.ys.utils.StringUtils;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>@description:</p>
 *
 * @projectName: Spring-Mini
 * @packageName: com.com.ys.bean.factory
 * @className:
 * @author: shuangyang
 * @date: 1/14/17 9:31 PM
 */
public abstract class AbstractBeanFactory implements BeanFactory, BeanDefinitionRegistry {

    //bean定义和bean对象的键值对
    private final ConcurrentHashMap<BeanDefinition, Object> beanCollection = new ConcurrentHashMap<BeanDefinition, Object>();
    //bean的名字和bean的定义集合
    private final ConcurrentHashMap<String, BeanDefinition> definitionBeanNameMap = new ConcurrentHashMap<String, BeanDefinition>();
    //bean的类和bean定义的集合
    private final ConcurrentHashMap<Class<?>, BeanDefinition> definitionBeanClassMap = new ConcurrentHashMap<Class<?>, BeanDefinition>();

    @Override
    public <T> T getBeanByName(String beanName) throws BeansException {
        //1.尝试从definitionBeanNameMap集合获取
        T object = doGetBean(beanName);
        if (object != null) {
            return object;
        }
        //2.当查找不到时,通过bean定义初始化一个bean对象
        //2.1查找bean定义
        BeanDefinition definition = getBeanDefinitionByName(beanName);
        if (definition == null) {
            throw new NoSuchBeanDefinitionException("not found bean name is " + beanName + " bean defintion");
        }
        //2.2查询到bean定义初始化bean
        Object bean = initBean(definition);
        return (T) bean;
    }

    /**
     * 根据class类型获取bean实例；
     *
     * @param requiredType
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    @Override
    public <T> T getBeanByType(Class<T> requiredType) throws BeansException {
        //1. 尝试从map中获取
        T object = doGetBean(requiredType);
        if (object != null) {
            return object;
        }
        //2.通过bean定义初始化一个bean对象
        //2.1查找bean定义
        BeanDefinition definition = getBeanDefinitionByClass(requiredType);
        if (definition == null) {
            throw new NoSuchBeanDefinitionException("not found bean class is " + requiredType.getName() + " bean definition");
        }
        Object bean = initBean(definition);
        return (T) bean;
    }

    /**
     * 通过beanName获取bean对象
     * Returns the object
     *
     * @param key
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    private <T> T doGetBean(Object key) {
        Optional<Map.Entry<BeanDefinition, Object>> optional = beanCollection.entrySet().stream().filter(item -> item.getKey().getBeanName().equals(key)).findAny();
        if (optional.isPresent()) {
            return (T) optional.get().getValue();
        }
        return null;
    }

    /**
     * 通过specified class 获取bean对象
     * Returns the object
     *
     * @param requiredType the class of specified type
     * @param <T>          the object of type T
     * @return
     */
    @SuppressWarnings("unchecked")
    private <T> T doGetBean(Class<T> requiredType) {
        for (Map.Entry<BeanDefinition, Object> entry : beanCollection.entrySet()) {
            if (entry.getKey().isMatchType(requiredType)) {
                return (T) entry.getValue();
            }
        }

        return null;
    }

    /**
     * 初始化bean的入口,包括bean对象以及内部属性赋值
     *
     * @param definition
     * @return
     * @throws InitializationException
     */
    private Object initBean(BeanDefinition definition) throws InitializationException {
        try {
            //根据bean定义初始化bean定义
            Object bean = generateBean(definition);
            //判断bean的模式
            if (definition.getScope() == BeanScope.SINGLETION) {//单例
                beanCollection.putIfAbsent(definition, bean);
                return bean;
            }
            if (definition.getScope() == BeanScope.PROTOTYPE) {//原型
                return bean;
            }
            return bean;
        } catch (Exception e) {
            throw new InitializationException("failed to initialization bean class is " + definition.getBeanClass().getName(), e);
        }
    }

    /**
     * 生成bean定义包括生成属性
     *
     * @param definition
     * @return
     * @throws Exception
     */
    protected abstract Object generateBean(BeanDefinition definition) throws Exception;

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition definition) throws BeanDefinitionStoreException {
        if (StringUtils.isEmpty(beanName)) {
            throw new BeanDefinitionStoreException("Bean name must not be empty");
        }
        if (StringUtils.isNull(definition)) {
            throw new BeanDefinitionStoreException("BeanDefinition must not be null");
        }
        if (definitionBeanClassMap.containsKey(definition.getBeanClass())) {
            return;
        }
        if (definitionBeanNameMap.containsKey(definition.getBeanName())) {
            throw new BeanDefinitionStoreException("already has bean name " + definition.getBeanName());
        }
        definitionBeanNameMap.put(definition.getBeanName(), definition);
        definitionBeanClassMap.put(definition.getBeanClass(), definition);
    }

    /**
     * 根据beanName获取bean定义
     *
     * @param beanName
     * @return
     * @throws BeanDefinitionStoreException
     */
    @Override
    public BeanDefinition getBeanDefinitionByName(String beanName) throws BeanDefinitionStoreException {
        for (Map.Entry<String, BeanDefinition> entry : definitionBeanNameMap.entrySet()) {
            if (entry.getKey().equals(beanName)) {
                return entry.getValue();
            }
        }
        return null;
    }

    @Override
    public BeanDefinition getBeanDefinitionByClass(Class<?> requiredType) throws NoSuchBeanDefinitionException {
        for (Map.Entry<Class<?>, BeanDefinition> entry : definitionBeanClassMap.entrySet()) {
            if (entry.getKey().isAssignableFrom(requiredType)) {
                return entry.getValue();
            }
        }
        return null;
    }

    @Override
    public boolean isBeanNameInUse(String beanName) {

        for (Map.Entry<String, BeanDefinition> entry : definitionBeanNameMap.entrySet()) {
            if (entry.getKey().equals(beanName)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        for (Map.Entry<String, BeanDefinition> entry : definitionBeanNameMap.entrySet()) {
            if (entry.getKey().equals(beanName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 移除bean定义
     *
     * @param beanName
     * @throws BeanDefinitionStoreException
     */
    @Override
    public void removeBeanDefinition(String beanName) throws BeanDefinitionStoreException {
        if (StringUtils.isEmpty(beanName)) {
            throw new BeanDefinitionStoreException("Bean name must not be empty");
        }
        if (!definitionBeanNameMap.containsKey(beanName)) {
            throw new BeanDefinitionStoreException("not found bean name is " + beanName);
        }
        //移除
        definitionBeanNameMap.remove(beanName);
    }

    @Override
    public int getBeanDefinitionCount() {
        return beanCollection.size();
    }
}
